'''Module: msg

A subclass of `str` that supports simple encryption/decryption.
'''
import string

def find_coprime(a, b=26):
    x, lastx = 0, 1
    y, lasty = 1, 0
    g, g1 = a, b
    
    while g1:
        quotient = g // g1
        g, g1 = g1, g - quotient * g1
        x, lastx = lastx - quotient*x, x
        y, lasty = lasty - quotient*y, y
    if g != 1:
        raise ValueError("{} is not coprime with the alphabet length ({})".format(a,b))
    return lastx if lastx > 0 else lastx + b

class Msg(str):
    '''A subclass of `str` that supports simple encryption/decryption.'''
    def atbash(self):
        trans = str.maketrans(
            string.ascii_lowercase+string.ascii_uppercase,
            string.ascii_lowercase[::-1]+string.ascii_uppercase[::-1])
        return Msg(self.translate(trans))

    def encode_affine(self, a, b):
        """
        >>> Msg('PROGRAMMINGPraxis123').encode_affine(5, 8)
        'FPAMPIQQWVMFpitwu123'
        >>> Msg('FpampiqqwvmFPITWU321').encode_affine(6, 8)
        Traceback (most recent call last):
        ...
        ValueError: 6 is not coprime with the alphabet length (26)
        """
        m = find_coprime(a)
        tr_plain = string.ascii_lowercase
        tr_cipher = ''.join(tr_plain[(i*a + b) % len(tr_plain)] for i in range(len(tr_plain)))
        trans = str.maketrans(tr_plain + tr_plain.upper(), tr_cipher + tr_cipher.upper())
        return self.translate(trans)

    def decode_affine(self, a, b):
        """
        >>> Msg('FpampiqqwvmFPITWU321').decode_affine(5, 8)
        'ProgrammingPRAXIS321'
        >>> Msg('FpampiqqwvmFPITWU321').decode_affine(6, 8)
        Traceback (most recent call last):
        ...
        ValueError: 6 is not coprime with the alphabet length (26)
        """
        m = find_coprime(a)
        return self.encode_affine(m, m * -b)
